/* 
 * Cashbox is the legal property of its developers, whose names are listed in the copyright file included
 * with this source distribution.
 * 
 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
 * General Public License as published by the Free Software Foundation; either version 2 of the License,
 * or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
 * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
 * Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along with this program; if not,
 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

#import "CBAccountViewController.h"
#import "CBDataController.h"

#import "CBFlippedView.h"
#import "AINoisyTableColumn.h"
#import "CBCurrencyFormatter.h"
#import "CBMixedStateSwitchCell.h"
#import "CBTransferPanelController.h"

#import <Cashbox/CBAccount.h>
#import <Cashbox/CBTransaction.h>
#import <Cashbox/CBAttribute.h>
#import <Cashbox/CBAttributeInfo.h>
#import <Cashbox/CBLabel.h>
#import <Cashbox/CBApplication.h>

#import "CBInterfacePreferenceViewController.h"

#define LIST_AUTOSAVE_NAME \
	[NSString stringWithFormat:@"%@ %@", [self autosaveName], [[self account] name]]
#define GRADIENT_TOP \
	[NSColor colorWithCalibratedRed:0.71372549 green:0.75294118 blue:0.81568627 alpha:1]
#define GRADIENT_BOTTOM \
	[NSColor colorWithCalibratedRed:0.79215686 green:0.81960784 blue:0.86666667 alpha:1]

static void *CBTransactionSortDescriptorsChangeContext = @"CBTransactionSortDescriptorsChangeContext";
static void *CBAlternatingRowPreferenceChangeContext = @"CBAlternatingRowPreferenceChangeContext";
static void *CBPostedStatePreferenceChangeContext = @"CBPostedStatePreferenceChangeContext";
static void *CBTransactionScrollingContext = @"CBTransactionScrollingContext";

@interface CBAccountViewController (Private)
- (void)rebindTransactions;
- (void)updateTableColumns;
- (void)scrollToMostRecent;

- (void)showTableColumn:(NSTableColumn *)column;
- (void)hideTableColumn:(NSTableColumn *)column;
- (NSArray *)dynamicTableColumns;
- (void)setDynamicTableColumns:(NSArray *)cols;

- (void)beginConstantScrollToEnd;
- (void)endConstantScrollToEnd;
@end

@implementation CBAccountViewController

+ (NSString *)nibName {
	return @"Account";
}

- (void)dealloc {
	[[NSNotificationCenter defaultCenter] removeObserver:self];
	
	[[NSUserDefaults standardUserDefaults] removeObserver:self forKeyPath:CBAlternatingRowPreferenceKey];
	[[NSUserDefaults standardUserDefaults] removeObserver:self forKeyPath:CBPostedStatePreferenceKey];

	[tableView removeObserver:self forKeyPath:@"sortDescriptors"];
	[self setDynamicTableColumns:nil];
	
	[account release];
	[autosaveName release];
	
	[super dealloc];
}

- (void)awakeFromNib {
	[tableView setIntercellSpacing:NSMakeSize(0, 3)];
	[tableView setTrackMouseEvents:TRUE];
	
	[gradientView setDrawsBackground:YES];
	[gradientView setBackgroundGradient:
	 [AIGradient gradientWithFirstColor:GRADIENT_TOP
							secondColor:GRADIENT_BOTTOM
							  direction:AIVertical]];

	// observe changes to the user defaults
	[[NSUserDefaults standardUserDefaults] addObserver:self
											forKeyPath:CBPostedStatePreferenceKey
											   options:0
											   context:CBPostedStatePreferenceChangeContext];
	[[NSUserDefaults standardUserDefaults] addObserver:self
											forKeyPath:CBAlternatingRowPreferenceKey
											   options:0
											   context:CBAlternatingRowPreferenceChangeContext];

	// observe sort descriptor changes
	[tableView addObserver:self
				forKeyPath:@"sortDescriptors"
				   options:0
				   context:CBTransactionSortDescriptorsChangeContext];	
	
	// set preferences
	[tableView setUsesAlternatingRowBackgroundColors:
	 [[NSUserDefaults standardUserDefaults] boolForKey:CBAlternatingRowPreferenceKey]];
	
	// register to receive notifications about text changing in the table view
	[[NSNotificationCenter defaultCenter] addObserver:self
											 selector:@selector(tableViewTextDidBeginEditing:)
												 name:NSTextDidBeginEditingNotification object:tableView];
	[[NSNotificationCenter defaultCenter] addObserver:self
											 selector:@selector(tableViewTextDidEndEditing:)
												 name:NSTextDidEndEditingNotification object:tableView];

	// set up default formatters
	[[[tableView tableColumnWithIdentifier:@"total"] dataCell]
	 setFormatter:[[[CBCurrencyFormatter alloc] init] autorelease]];
	
	[balanceFormatter setPositivePrefix:
	 [NSString stringWithFormat:FSLocalizedString(@"Balance: %@", nil), [balanceFormatter positivePrefix]]];
	[balanceFormatter setNegativePrefix:
	 [NSString stringWithFormat:FSLocalizedString(@"Balance: %@", nil), [balanceFormatter negativePrefix]]];
	[balanceFormatter setPositiveSuffix:
	 [NSString stringWithFormat:FSLocalizedString(@"%@", nil), [balanceFormatter positiveSuffix]]];
	[balanceFormatter setNegativeSuffix:
	 [NSString stringWithFormat:FSLocalizedString(@"%@", nil), [balanceFormatter negativeSuffix]]];	
	[countFormatter setPositivePrefix:
	 [NSString stringWithFormat:FSLocalizedString(@"%@", nil), [countFormatter positivePrefix]]];
	[countFormatter setPositiveSuffix:
	 [NSString stringWithFormat:FSLocalizedString(@"%@ Transactions", nil), [countFormatter positiveSuffix]]];

	// setup transactions array controller
	[self rebindTransactions];
	
	// setup labels array controller
	[labels setManagedObjectContext:[[cashbox dataController] managedObjectContext]];
	[labels setEntityName:[CBLabel entityName]];
	[labels setSortDescriptors:
	 [NSArray arrayWithObject:[[[NSSortDescriptor alloc] initWithKey:@"index" ascending:YES] autorelease]]];
	[labels fetchWithRequest:nil merge:NO error:NULL];
}

- (void)observeValueForKeyPath:(NSString *)keyPath
					  ofObject:(id)object
						change:(NSDictionary *)change
					   context:(void *)context {
	if (context == CBPostedStatePreferenceChangeContext) {
		for (NSTableColumn *column in [tableView tableColumns]) {
			if ([[column dataCell] class] == [CBMixedStateSwitchCell class]) {
				[[column dataCell] setAllowsMixedState:
				 [[NSUserDefaults standardUserDefaults] boolForKey:CBPostedStatePreferenceKey]];
			}
		}
	} else if (context == CBAlternatingRowPreferenceChangeContext) {
		BOOL alternate = [[[NSUserDefaults standardUserDefaults]
						   objectForKey:CBAlternatingRowPreferenceKey] boolValue];
		[tableView setUsesAlternatingRowBackgroundColors:alternate];
	} else if (context == CBTransactionScrollingContext) {
		[tableView scrollRowToVisible:[tableView numberOfRows]-1];
	} else if (context == CBTransactionSortDescriptorsChangeContext) {
		NSArray *descriptors = [tableView sortDescriptors];
		NSArray *standard = [NSArray arrayWithObjects:@"date", @"uniqueIdentifier", nil];
		NSArray *newDescriptors = [NSSortDescriptor applyStandardOrdering:standard
															toDescriptors:descriptors];
		if (newDescriptors) { [tableView setSortDescriptors:newDescriptors]; }
	}
}

- (CBAccount *)account {
	return account;
}

- (void)setAccount:(CBAccount *)anAccount {
	if (account != anAccount) {
		[balanceTextField unbind:@"value"];
		[countTextField unbind:@"value"];
		
		[account release];
		account = [anAccount retain];
		
		NSValueTransformer *transformer = [NSValueTransformer valueTransformerForName:@"CBIntFloatTransformer"];
		[balanceTextField bind:@"value"
					  toObject:account
				   withKeyPath:@"total"
					   options:[NSDictionary dictionaryWithObjectsAndKeys:
								transformer, NSValueTransformerBindingOption, nil]];
		[countTextField  bind:@"value"
					 toObject:account
				  withKeyPath:@"transactions.@count"
					  options:nil];

		[transactions unbind:@"contentSet"];
		[transactions setContent:nil];
		[self updateTableColumns];
		[self rebindTransactions];
		[self scrollToMostRecent];
	}
}

#pragma mark main window delegate

- (IBAction)addTransaction:(id)sender {
//	[[[[cashbox dataController] managedObjectContext] undoManager] beginUndoGrouping];
	Class transactionClass = [[[self account] class] transactionClass];
	CBTransaction *t = [[[transactionClass alloc] initAndInsertIntoAccount:[self account]] autorelease];
	[transactions setSelectedObjects:[NSArray arrayWithObjects:t, nil]];
	int editIndex = [[tableView tableColumns] indexOfObject:[[self dynamicTableColumns] firstObject]];
	if (editIndex != NSNotFound) {
		[tableView editColumn:editIndex row:[transactions selectionIndex] withEvent:nil select:YES];
	} else { [tableView scrollRowToVisible:[transactions selectionIndex]]; }
//	[[[[cashbox dataController] managedObjectContext] undoManager] endUndoGrouping];
//	[[[[cashbox dataController] managedObjectContext] undoManager]
//  setActionName:FSLocalizedString(@"New Transaction", @"Undo action name")];
}

- (BOOL)canAddTransaction {
	return YES;
}

- (IBAction)addTransferTransaction:(id)sender {
	[transferPanel setAccount:[self account]];
	[NSApp beginSheet:[transferPanel window]
	   modalForWindow:[[self view] window]
		modalDelegate:self
	   didEndSelector:@selector(finishAddingTransfer:returnCode:contextInfo:)
		  contextInfo:nil];
}

- (void)finishAddingTransfer:(NSWindow *)sheet returnCode:(int)returnCode  contextInfo:(void  *)contextInfo {
	if (returnCode == NSAlertDefaultReturn) {
		long long amount = [transferPanel transferAmount];
		CBTransferDirection direction = [transferPanel transferDirection];
		
		Class transferClass = [[[transferPanel transferAccount] class] transactionClass];
		Class actuallClass = [[[self account] class] transactionClass];
		CBTransaction *transfer = [[[transferClass alloc] initAndInsertIntoAccount:
									[transferPanel transferAccount]] autorelease];
		CBTransaction *actual = [[[actuallClass alloc] initAndInsertIntoAccount:
								  [self account]] autorelease];
		
		[transfer setDate:nil];
		[actual setTransfer:transfer];
		if (direction == CBTransferTo) {
			[actual setValue:[NSNumber numberWithLongLong:amount]
					 forRole:CBNegativeCashflowRole];
		} else if (direction == CBTransferFrom) {
			[actual setValue:[NSNumber numberWithLongLong:amount]
					 forRole:CBPositiveCashflowRole];
		}

		[transactions setSelectedObjects:[NSArray arrayWithObject:actual]];
		int editIndex = [[tableView tableColumns] indexOfObject:
						 [[self dynamicTableColumns] firstObject]];
		if (editIndex != NSNotFound) {
			[tableView editColumn:editIndex
				row:[transactions selectionIndex]
						withEvent:nil
						   select:YES];
		}
		else { [tableView scrollRowToVisible:[transactions selectionIndex]]; }
	}
}

- (BOOL)canAddTransferTransaction {
	CBAttributeInfo *info = [[[[self account] class] transactionClass] attributeInfo];
	return [info attributeForRole:CBPositiveCashflowRole] || 
		[info attributeForRole:CBNegativeCashflowRole];
}

- (IBAction)removeTransaction:(id)sender {
	CBTransaction *selection, *nextSelection;
	int selectionIndex, nextSelectionIndex;
	
	selectionIndex = [transactions selectionIndex];
	if (selectionIndex != NSNotFound) {
		if (selectionIndex == 0) { nextSelectionIndex = 0; }
		else if (selectionIndex == [[transactions arrangedObjects] count] - 1) {
			nextSelectionIndex = selectionIndex - 1;
		} else { nextSelectionIndex = selectionIndex + 1; }

		selection = [[transactions arrangedObjects] objectAtIndex:selectionIndex];
		nextSelection = [[transactions arrangedObjects] objectAtIndex:nextSelectionIndex];
		[[[cashbox dataController] managedObjectContext] deleteObject:selection]; // delete the object
		[transactions setSelectedObjects:[NSArray arrayWithObject:nextSelection]]; // select something close
	}
}

- (BOOL)canRemoveTransaction {
	return [[transactions selectedObjects] count] > 0;
}

#pragma mark table columns
// ----------------------------------------------------------------------------------------------------
// table columns
// ----------------------------------------------------------------------------------------------------

- (void)showTableColumn:(NSTableColumn *)column {
	if ([NSApp isOnLeopardOrBetter]) {
		[(id)column setHidden:FALSE];
		return;
	}	
	[self willChangeValueForKey:@"visibleTableColumns"];
	[tableView addTableColumn:column];
	[self didChangeValueForKey:@"visibleTableColumns"];
}

- (void)hideTableColumn:(NSTableColumn *)column {
	if ([NSApp isOnLeopardOrBetter]) {
		[(id)column setHidden:TRUE];
		return;
	}	
	[self willChangeValueForKey:@"visibleTableColumns"];
	[tableView removeTableColumn:column];
	[self didChangeValueForKey:@"visibleTableColumns"];
}

- (NSArray *)visibleTableColumns {
	return [tableView tableColumns];
}

- (NSArray *)availableTableColumns {
	return dynamicTableColumns;
}

- (void)toggleTableColumnVisibility:(NSTableColumn *)column {
	if ([NSApp isOnLeopardOrBetter]) {
		[(id)column setHidden:![(id)column isHidden]];
		return;
	}

	if ([[self availableTableColumns] indexOfObjectIdenticalTo:column] != NSNotFound) {
		if ([[self visibleTableColumns] indexOfObjectIdenticalTo:column] == NSNotFound) {
			[tableView sizeLastColumnToFit];

			float makeSpace = [column width];
			NSTableColumn *moveColumn;
			NSEnumerator *enumerator = [[tableView tableColumns] reverseObjectEnumerator];
			while (moveColumn = [enumerator nextObject]) {
				if (makeSpace <= 0) { break; } // made enough space
				
				float currentWidth = [moveColumn width]; // width of last column
				float minWidth = [moveColumn minWidth]; // min width that last column allows
				float changeWidth = currentWidth - minWidth; // max change that the last column could do
				
				// resize either the max (if we need that)
				// or less (if we don't need that much space)
				if (changeWidth > makeSpace) { changeWidth = makeSpace; }
				
				makeSpace -= changeWidth; // we made some space
				[moveColumn setWidth:currentWidth - changeWidth]; // resize the column
			}
			
			[column setWidth:0];			 // the width will now change
			[self showTableColumn:column];	 // after showing the table column
			[tableView sizeLastColumnToFit]; // which will force an autosave
		} else {
			[self hideTableColumn:column];
			[tableView sizeLastColumnToFit];
		}
	}
}

- (NSArray *)dynamicTableColumns {
	return dynamicTableColumns;
}

- (void)setDynamicTableColumns:(NSArray *)cols {
	if (cols != dynamicTableColumns) {
		[self willChangeValueForKey:@"availableTableColumns"];
		[self willChangeValueForKey:@"dynamicTableColumns"];
		[dynamicTableColumns release];
		dynamicTableColumns = [cols copy];
		[self didChangeValueForKey:@"dynamicTableColumns"];
		[self didChangeValueForKey:@"availableTableColumns"];
	}
}

- (void)updateTableColumns {	

	// declare some things
	NSTableColumn *column;
	NSEnumerator *tableColumnEnumerator;
	NSMutableArray *dynamicColumns;

	// save previos config and wait until
	// everything is added in this config to set the
	// autosave name again
	[self willChangeValueForKey:@"visibleTableColumns"];
	[tableView setAutosaveTableColumns:NO];
	// clear out the sort descriptors (cocoa keeps track of the ones assigned manually)
	// and things start to act funky when they're not cleared
	[tableView setSortDescriptors:nil]; 

	// remove all table columns
	tableColumnEnumerator = [[[tableView tableColumns] copy] objectEnumerator];
	while (column = [tableColumnEnumerator nextObject]) { [tableView removeTableColumn:column]; }
	
	// arrays of columns
	dynamicColumns = [NSMutableArray array];
	
	if ([self account]) {
		// add dynamic table columns based on attributes
		NSTableColumn *column;
		NSString *key;
		Class transactionClass = [[[self account] class] transactionClass];
		NSArray *keys = [[[transactionClass attributeInfo] names]
						 sortedArrayUsingFunction:(NSInteger (*)(id, id, void *))attributeKeySort
										  context:transactionClass];
		NSEnumerator *keyEnumerator = [keys objectEnumerator];
		while (key = [keyEnumerator nextObject]) {
			NSFormatter *formatter = nil;
			NSCell *cell = nil;
			Class transactionClass = [[[self account] class] transactionClass];
			CBAttribute *attribute  = [[transactionClass attributeInfo] attributeWithName:key];
			CBAttributeType type = [attribute type];
			CBAttributeOptions options = [attribute options];
			
			Class columnClass = [NSTableColumn class];
			if (type == CBDateAttribute) {
				columnClass = [AINoisyTableColumn class];
			}
				
			column = [[[columnClass alloc] initWithIdentifier:key] autorelease];
			[[column headerCell] setTitle:[attribute localizedName]];
			
			// set up bindings
			NSString *bind = @"value";
			NSMutableDictionary *bindingOptions = 
				[NSMutableDictionary dictionaryWithObjectsAndKeys:		
				 [NSNumber numberWithBool:TRUE], NSCreatesSortDescriptorBindingOption,		
				 [NSNumber numberWithBool:TRUE], NSRaisesForNotApplicableKeysBindingOption,		
				 [NSNumber numberWithBool:TRUE], NSConditionallySetsEditableBindingOption,		
				 nil];
			if (type == CBCurrencyAttribute) {		
				[bindingOptions setObject:[NSValueTransformer valueTransformerForName:@"CBIntFloatTransformer"]
								   forKey:NSValueTransformerBindingOption];
			}
			if (type == CBMixedAttribute || options & CBReadonlyAttribute) {
				[bindingOptions setObject:[NSNumber numberWithBool:FALSE]
								   forKey:NSConditionallySetsEditableBindingOption];
			}

			// set attributes on the columns and cell types
			if (type == CBMixedAttribute) {
				[column setWidth:20];
				[column setMinWidth:20];
				[column setMaxWidth:25];
				cell = [[[CBMixedStateSwitchCell alloc] init] autorelease];
				[cell setTitle:@""];
				[cell setAllowsMixedState:
				 [[NSUserDefaults standardUserDefaults] boolForKey:CBPostedStatePreferenceKey]];
			} else if (type == CBCurrencyAttribute) {
				[column setWidth:70];
				[column setMinWidth:20];
				cell = [[[FSOvalTextFieldCell alloc] init] autorelease];
				[cell setAlignment:NSRightTextAlignment];
				formatter = [[[CBCurrencyFormatter alloc] init] autorelease];
			} else if (type == CBIntegerAttribute) {
				[column setWidth:80];
				[column setMinWidth:40];
				NSNumberFormatter *numberFormatter = [[[NSNumberFormatter alloc] init] autorelease];
				[numberFormatter setAllowsFloats:NO];
				formatter = numberFormatter;
				cell = [[[FSOvalTextFieldCell alloc] init] autorelease];
			} else if (type == CBDateAttribute) {
				[column setWidth:90];
				[column setMinWidth:50];
				NSDateFormatter *dateFormatter = [[[NSDateFormatter alloc] init] autorelease];
				[dateFormatter setDateStyle:NSDateFormatterMediumStyle];
				[dateFormatter setLenient:YES];
				formatter = dateFormatter;
				cell = [[[FSQuickChangeDateCell alloc] init] autorelease];
			} else if (type == CBStringAttribute) {
				[column setWidth:80];
				[column setMinWidth:50];
				cell = [[[FSOvalTextFieldCell alloc] init] autorelease];
				formatter = [[[FSAutocompleteFormatter alloc] init] autorelease];
				[(FSAutocompleteFormatter *)formatter setDataSource:self];
			} else if (type == CBBooleanAttribute) {
				cell = [[[NSButtonCell alloc] init] autorelease];
				[(NSButtonCell *)cell setButtonType:NSSwitchButton];
				[cell setControlSize:NSSmallControlSize];
				[cell setTitle:nil];
				[column setWidth:20];
				[column setMinWidth:20];
			} else if (type == CBLabelAttribute) {
				cell = [[[NSPopUpButtonCell alloc] init] autorelease];
				[cell setControlSize:NSSmallControlSize];
				[cell setBordered:NO];
				[column setWidth:100];
				[column setMaxWidth:1000];
				[column setMinWidth:20];
				NSDictionary *menuBindingOptions =
					[NSDictionary dictionaryWithObjectsAndKeys:		
					 [NSNumber numberWithBool:TRUE], NSInsertsNullPlaceholderBindingOption,		
					 [NSNumber numberWithBool:TRUE], NSRaisesForNotApplicableKeysBindingOption,		
					 nil];
				[cell bind:@"content"
					toObject:labels	
				 withKeyPath:[NSString stringWithFormat:@"arrangedObjects"]		
					 options:menuBindingOptions];					
				[cell bind:@"contentValues"
					toObject:labels		
				 withKeyPath:[NSString stringWithFormat:@"arrangedObjects.name"]		
					 options:menuBindingOptions];
				bind = @"selectedObject";
			}

			if (options & CBReadonlyAttribute) {
				[cell setEditable:NO];
			}
			if (options & CBLongTextAttribute) {
				[column setWidth:[column width] + 70];
				[column setMaxWidth:1000];
			}
			
			if (cell) { [column setDataCell:cell]; }
			[[column dataCell] setFormatter:formatter];
			[column bind:bind
				toObject:transactions		
			 withKeyPath:[NSString stringWithFormat:@"arrangedObjects.%@", key]		
				 options:bindingOptions];				
			[tableView addTableColumn:column];
		}
		
		// set the dynamic columns we just calculated
		[self setDynamicTableColumns:[tableView tableColumns]];
		
		[self tableViewColumnDidResize:nil];
		if ([self autosaveName]) {
			[tableView setAutosaveName:LIST_AUTOSAVE_NAME];
			[tableView setAutosaveTableColumns:YES];
		}
		[tableView sizeLastColumnToFit];
		
		if ([[tableView sortDescriptors] count] == 0) {
			[tableView setSortDescriptors:
			 [NSArray arrayWithObjects:
			  [NSSortDescriptor descriptorWithKey:@"date" ascending:YES],
			  [NSSortDescriptor descriptorWithKey:@"uniqueIdentifier" ascending:YES], nil]];
		}
	}
	
	[self didChangeValueForKey:@"visibleTableColumns"];
}

#pragma mark when the account changes
// ----------------------------------------------------------------------------------------------------
// when the account changes
// ----------------------------------------------------------------------------------------------------

- (void)rebindTransactions {
	CBAccount *anAccount = [self account];
	if (anAccount) {
		// faster (threaded) fetching of transactions
		[transactions unbind:@"contentSet"];
		[transactions setEntityName:[CBTransaction entityName]];
		[transactions setFetchPredicate:[NSPredicate predicateWithFormat:@"account = %@", anAccount]];
		[transactions setManagedObjectContext:[[cashbox dataController] managedObjectContext]];
		[transactions fetch:nil];
	}
}


#pragma mark scrolling to most recent
// ----------------------------------------------------------------------------------------------------
// scrolling to most recent
// ----------------------------------------------------------------------------------------------------

- (void)scrollToMostRecent {
	NSArray *descriptors = [transactions sortDescriptors];
	NSSortDescriptor *primary = [descriptors firstObject];
	if ([[primary key] isEqualToString:@"date"] && [primary ascending]) {
		[self beginConstantScrollToEnd];
	} else {
		[tableView scrollRowToVisible:0];
	}
}

- (void)beginConstantScrollToEnd {
	continueScrolling = TRUE;
	[transactions addObserver:self
				   forKeyPath:@"arrangedObjects"
					  options:0
					  context:CBTransactionScrollingContext];
	[(CBApplication *)NSApp dispatchEventsToTarget:self
											action:@selector(eventOccurred:)];	
}

- (void)endConstantScrollToEnd {
	continueScrolling = FALSE;
	[transactions removeObserver:self forKeyPath:@"arrangedObjects"];
	[(CBApplication *)NSApp stopDispatchingEvents];	
}

- (void)eventOccurred:(NSEvent *)event {
	if ([event type] == NSLeftMouseDown ||
		[event type] == NSRightMouseDown ||
		[event type] == NSKeyDown ||
		[event type] == NSOtherMouseDown ||
		[event type] == NSScrollWheel) {
		
		[self endConstantScrollToEnd];
	}
}

#pragma mark updating transaction changing
// ----------------------------------------------------------------------------------------------------
// transaction changing
// ----------------------------------------------------------------------------------------------------

- (void)controlTextDidBeginEditing:(NSNotification *)notification {
#warning add back in somehow
// bug was add two transactions in a row
//	[[[[cashbox dataController] managedObjectContext] undoManager] beginUndoGrouping];
}

- (void)controlTextDidEndEditing:(NSNotification *)notification {
	[transactions rearrangeObjects];

#warning add back in somehow
//	[[[[cashbox dataController] managedObjectContext] undoManager] endUndoGrouping];
//	[[[[cashbox dataController] managedObjectContext] undoManager]
//		setActionName:FSLocalizedString(@"Edit Transaction", @"Undo action name")];
}


#pragma mark autocompletion
// ----------------------------------------------------------------------------------------------------
// autocompletion
// ----------------------------------------------------------------------------------------------------

- (NSString *)completionForPrefix:(NSString *)prefix {
	
    if (!prefix || [prefix length] == 0) { return nil; }
	
	CBTransaction *transaction;
	NSString *candidate, *completion = nil;
	Class transactionClass = [[[self account] class] transactionClass];
	NSEnumerator *enumerator = [[transactions arrangedObjects] reverseObjectEnumerator];	
	while (!completion && (transaction = [enumerator nextObject])) {
		NSTableColumn *column;
		NSEnumerator *columns = [[tableView tableColumns] objectEnumerator];
		while (!completion && (column = [columns nextObject])) {
			NSString *key = [column identifier];
			CBAttribute *attribute  = [[transactionClass attributeInfo] attributeWithName:key];
			CBAttributeType type = [attribute type];
			if (type == CBStringAttribute) {
				candidate = [transaction valueForKey:key];
				if ([[candidate commonPrefixWithString:prefix
											   options:NSCaseInsensitiveSearch] length] == [prefix length]) {
					completion = candidate;
				}				
			}
		}
    }
	
    return completion;
}

- (BOOL)isACompletion:(NSString *)aString {
	
	CBTransaction *transaction;
	NSString *candidate;
	BOOL completion = FALSE;
	Class transactionClass = [[[self account] class] transactionClass];
	NSEnumerator *enumerator = [[transactions arrangedObjects] reverseObjectEnumerator];	
	while (!completion && (transaction = [enumerator nextObject])) {
		NSTableColumn *column;
		NSEnumerator *columns = [[tableView tableColumns] objectEnumerator];
		while (!completion && (column = [columns nextObject])) {
			NSString *key = [column identifier];
			CBAttribute *attribute  = [[transactionClass attributeInfo] attributeWithName:key];
			CBAttributeType type = [attribute type];
			if (type == CBStringAttribute) {
				candidate = [transaction valueForKey:key];
				if ([candidate caseInsensitiveCompare:aString] == NSOrderedSame) {
					completion = TRUE;
				}				
			}
		}
    }
		
    return completion;
}


#pragma mark autosavename
// ----------------------------------------------------------------------------------------------------
// autosavename
// ----------------------------------------------------------------------------------------------------

- (void)setAutosaveName:(NSString *)name {
	if (autosaveName != name) {
		[autosaveName release];
		autosaveName = [name retain];
		[self updateTableColumns];
	}
}

- (NSString *)autosaveName {
	return autosaveName;
}

#pragma mark table view delegate
// ----------------------------------------------------------------------------------------------------
// table view delegate
// ----------------------------------------------------------------------------------------------------

- (BOOL)tableView:(NSTableView *)tableView shouldEditTableColumn:(NSTableColumn *)tableColumn row:(int)row {
	return [[[transactions arrangedObjects] objectAtIndex:row] state] == CBInitialState;
}

- (void)tableView:(NSTableView *)aTableView
  willDisplayCell:(id)aCell
   forTableColumn:(NSTableColumn *)aTableColumn
			  row:(int)rowIndex {
	if ([aCell isKindOfClass:[FSOvalTextFieldCell class]]) {
		int column = [[tableView tableColumns] indexOfObject:aTableColumn];
		NSTableColumn *previousColumn = ((column > 0) ?
										 [[tableView tableColumns] objectAtIndex:column - 1] : nil);
		NSTableColumn *nextColumn = ((column + 1 < [[tableView tableColumns] count]) ?
									 [[tableView tableColumns] objectAtIndex:column + 1] : nil);
		
		CBTransaction *transaction = [[transactions arrangedObjects] objectAtIndex:rowIndex];
		NSColor *color = [[transaction label] color];
		if ([transaction state] != CBInitialState) {
			[aCell setTextColor:[NSColor grayColor]];
			[aCell setOvalColor:[color darkenAndAdjustSaturationBy:-0.10]];
		} else {
			[aCell setTextColor:[NSColor blackColor]];
			[aCell setOvalColor:color];
		}

		if (!previousColumn || ![[previousColumn dataCell] isKindOfClass:[FSOvalTextFieldCell class]]) {
			[aCell setDrawsLeftOval:TRUE];
		} else { [aCell setDrawsLeftOval:FALSE]; }
		if (!nextColumn || ![[nextColumn dataCell] isKindOfClass:[FSOvalTextFieldCell class]]) {
			[aCell setDrawsRightOval:TRUE];
		} else { [aCell setDrawsRightOval:FALSE]; }
	} else if ([aCell respondsToSelector:@selector(setEnabled:)] &&
			   ![[aTableColumn identifier] isEqualToString:@"state"]) {
		[aCell setEnabled:[[[transactions arrangedObjects] objectAtIndex:rowIndex] state] == CBInitialState];
	}
}

- (void)tableViewColumnDidMove:(NSNotification *)notification {
	[tableView display];
}

- (void)tableViewColumnDidResize:(NSNotification *)aNotification {
	/* Code from Adium */
	NSTableColumn *column = [[aNotification userInfo] objectForKey:@"NSTableColumn"];
	
	if (!aNotification ||
		([[[column dataCell] formatter] isKindOfClass:[NSDateFormatter class]])) {
		NSDateFormatter *dateFormatter;
		NSCell *cell = [column dataCell];
		
		[cell setObjectValue:[NSDate dateWithString:@"2008-12-12 00:00:00 +0000"]];
		
		float width = [column width];
		
#define NUMBER_DATE_STYLES	4
		NSDateFormatterStyle formatterStyles[NUMBER_DATE_STYLES] = {
			NSDateFormatterFullStyle,
			NSDateFormatterLongStyle,
			NSDateFormatterMediumStyle,
			NSDateFormatterShortStyle
		};
		float requiredWidth;
		
		dateFormatter = [cell formatter];
		if (!dateFormatter) {
			dateFormatter = [[[NSDateFormatter alloc] init] autorelease];
			[dateFormatter setFormatterBehavior:NSDateFormatterBehavior10_4];
			[dateFormatter setTimeStyle:NSDateFormatterNoStyle];
			[cell setFormatter:dateFormatter];
		}
		
		requiredWidth = width + 1;
		int i;
		for (i = 0; (i < NUMBER_DATE_STYLES) && (requiredWidth > width); i++) {
			[dateFormatter setDateStyle:formatterStyles[i]];
			requiredWidth = [cell cellSizeForBounds:NSMakeRect(0,0,1e6,1e6)].width;
			// Require a bit of space so the date looks comfortable. Very long dates
			// relative to the current date can still overflow...
			requiredWidth += 3;					
		}
	}
}


#pragma mark split view delegate
// ----------------------------------------------------------------------------------------------------
// split view delegate
// ----------------------------------------------------------------------------------------------------

	- (float)splitView:(NSSplitView *)sender
constrainMinCoordinate:(float)proposedCoord
		   ofSubviewAt:(int)offset {
	return 75; 
	return proposedCoord;
}

	- (float)splitView:(NSSplitView *)sender
constrainMaxCoordinate:(float)proposedCoord
	   ofSubviewAt:(int)offset {
	return [sender frame].size.height - 150;
	return proposedCoord;
}

	- (float)splitView:(NSSplitView *)sender
constrainSplitPosition:(float)proposedPosition
		   ofSubviewAt:(int)offset {
	return proposedPosition;
}

- (void)splitView:(NSSplitView *)sender resizeSubviewsWithOldSize:(NSSize)oldSize {
	NSArray *subviews = [sender subviews];
	NSView *first = [subviews objectAtIndex:0];
	NSView *second = [subviews objectAtIndex:1];
	
	float height = [sender frame].size.height - [second frame].size.height - [sender dividerThickness];
	if (height > 75) {			
		[second setFrameSize:NSMakeSize([sender frame].size.width, [second frame].size.height)];
		[first setFrameSize:NSMakeSize([sender frame].size.width, height)];
	} else {
		[first setFrameSize:NSMakeSize([sender frame].size.width, [first frame].size.height)];
		[second setFrameSize:
		 NSMakeSize([sender frame].size.width,
					[sender frame].size.height - [first frame].size.height - [sender dividerThickness])];
	}
	
	[sender adjustSubviews];
}

- (BOOL)splitView:(NSSplitView *)sender canCollapseSubview:(NSView *)subviews {
	return [[sender subviews] objectAtIndex:1] == subviews;
}

@end
